﻿using OpenCvSharp;
using System.Diagnostics;

namespace SearchForPicturesWithPictures.Common
{
    internal unsafe class SurfHelper : IDisposable
    {
        readonly OpenCvSharp.XFeatures2D.SURF _surf;
        public SurfHelper(double threshold)
        {
            _surf = OpenCvSharp.XFeatures2D.SURF.Create(threshold, 4, 3, true, true);
        }
        public void Dispose()
        {
            _src?.Dispose();
            _matSrcRet?.Dispose();
            _surf.Dispose();
        }
        public bool DrawMatches { get; set; }
        private Mat _src;
        private Mat _matSrcRet;
        private KeyPoint[] _keyPointsSrc;
        public void SerImageSrc(string imgSrc)
        {
            _src = new Mat(imgSrc);
            Cv2.CvtColor(_src, _src, ColorConversionCodes.BGR2BGRA);
            _matSrcRet = new Mat();
            _surf.DetectAndCompute(_src, null, out _keyPointsSrc, _matSrcRet);
        }
        /// <summary>
        /// 特征点Surf匹配
        /// </summary>
        /// <param name="imgSub">输入图2</param>
        /// <param name="similarityScore">相似度</param>
        /// <returns></returns>
        public Bitmap MatchPicBySurf(string imgSub, out float similarityScore)
        {
            similarityScore = 0;
            if (_src == null) return null;
            try
            {
                using Mat matTo = new Mat(imgSub);
                if (matTo.Empty())
                {
                    return null;
                }
                using Mat matToRet = new Mat();

                int channels = matTo.Channels();
                if (channels != 1 && channels != 3 && channels != 4)
                {
                    // 图像通道数不是1、3或4，这可能意味着图像是其他格式，如16位或浮点图像
                    Trace.WriteLine("Image has an unsupported number of channels.");
                    return null;
                }
                // 如果图像是灰度图，转换为BGR
                if (channels == 1)
                {
                    Cv2.CvtColor(matTo, matTo, ColorConversionCodes.GRAY2BGR);
                }

                _surf.DetectAndCompute(matTo, null, out var keyPointsTo, matToRet);

                using (var flnMatcher = new OpenCvSharp.FlannBasedMatcher())
                {
                    var matches = flnMatcher.Match(_matSrcRet, matToRet);
                    //求最小最大距离
                    double minDistance = 1000;//反向逼近
                    double maxDistance = 0;
                    fixed (DMatch* dm = matches)
                    {
                        for (int i = 0; i < _matSrcRet.Rows; i++)
                        {
                            double distance = (dm + i)->Distance;
                            if (distance > maxDistance)
                            {
                                maxDistance = distance;
                            }
                            if (distance < minDistance)
                            {
                                minDistance = distance;
                            }
                        }
                    }

                    var pointsSrc = new List<Point2f>(_matSrcRet.Rows);
                    var pointsDst = new List<Point2f>(_matSrcRet.Rows);
                    //筛选较好的匹配点
                    var goodMatches = new List<DMatch>(_matSrcRet.Rows);
                    fixed (DMatch* dm = matches)
                    {
                        fixed (KeyPoint* kps = _keyPointsSrc, kpt = keyPointsTo)
                        {
                            for (int i = 0; i < _matSrcRet.Rows; i++)
                            {
                                DMatch* dmTemp = (dm + i);
                                double distance = dmTemp->Distance;
                                if (distance < Math.Max(minDistance * 2, 0.02))
                                {
                                    pointsSrc.Add((kps + dmTemp->QueryIdx)->Pt);
                                    pointsDst.Add((kpt + dmTemp->TrainIdx)->Pt);
                                    //距离小于范围的压入新的DMatch
                                    goodMatches.Add(*dmTemp);
                                }
                            }
                        }
                    }

                    using var outMat = new Mat();
                    var pSrc = new List<Point2d>(pointsSrc.Count);
                    var pDst = new List<Point2d>(pointsDst.Count);
                    //单精度点转为双精度点
                    foreach (Point2f point in pointsSrc) { pSrc.Add(new Point2d(point.X, point.Y)); }
                    foreach (Point2f point in pointsDst) { pDst.Add(new Point2d(point.X, point.Y)); }
                    using var outMask = new Mat();
                    // 如果原始的匹配结果为空, 则跳过过滤步骤
                    if (pSrc.Count >= 4 && pDst.Count >= 4)
                        // 算法RANSAC对匹配的结果做过滤
                        Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask);
                    // 如果通过RANSAC处理后的匹配点大于10个,才应用过滤. 否则使用原始的匹配点结果(匹配点过少的时候通过RANSAC处理后,可能会得到0个匹配点的结果).
                    if (DrawMatches)
                    {
                        if (outMask.Rows > 10)
                        {
                            byte[] maskBytes = new byte[outMask.Rows * outMask.Cols];
                            outMask.GetArray(out maskBytes);
                            Cv2.DrawMatches(_src, _keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, matchesMask: maskBytes, flags: DrawMatchesFlags.NotDrawSinglePoints);
                        }
                        else
                            Cv2.DrawMatches(_src, _keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, flags: DrawMatchesFlags.NotDrawSinglePoints);
                    }

                    // 计算相似度分数
                    similarityScore = 0;
                    if (goodMatches.Count > 0)
                    {
                        // 使用匹配点的数量作为相似度的一个因素
                        similarityScore = goodMatches.Count;

                        // 计算平均匹配点质量
                        float totalDistance = goodMatches.Sum(match => match.Distance);
                        if (totalDistance > 0) // 确保分母不为零
                        {
                            // 使用平均匹配点质量作为相似度的另一个因素
                            similarityScore += 1 / (totalDistance / goodMatches.Count);
                        }
                        // 归一化相似度分数（如果有两个因素）
                        similarityScore /= 2;
                    }
                    //return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(outMat);
                    return null;
                }

            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message + "；" + ex.StackTrace);
                return null;
            }
        }
    }
}
